#include <wx/wx.h>
#include <map>

#include "pyjuku.h"
#include "pyfootprint.h"

#include "jukuapp.h"
#include "strfnd.h"
#include "ratsnest.h"
#include "cut.h"
#include "narrowcut.h"
#include "preferencesdialog.h"
#include "projectsettingsdialog.h"

#define pyapi_check_silent(x){\
	if(!(x)){\
		fprintf(stderr, "Python API: Test failed, aborting: " #x "\n");\
		Py_INCREF(Py_None);\
		return Py_None;\
	}\
}

PyObject* set_setting(PyObject *self, PyObject *args)
{
	const char *setting;
	const char *value;
	if(!PyArg_ParseTuple(args, "ss", &setting, &value)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	
	if(!wxGetApp().m_settings.SetSettingString(setting,
			wxString(value, wxConvUTF8)))
	{
		PyErr_SetString(PyExc_RuntimeError, "couldn't change setting");
		return NULL;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* get_setting(PyObject *self, PyObject *args)
{
	const char *setting;
	if(!PyArg_ParseTuple(args, "s", &setting)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	
	wxString value = wxGetApp().m_settings.GetSettingString(setting);

	return Py_BuildValue("s", (const char*)value.mb_str(wxConvUTF8));
}

//Internal function
static wxMenu* get_menu(wxString menupath)
{
    wxMenuBar *menubar = wxGetApp().m_frame->GetMenuBar();
	if(menubar == NULL){
		return NULL;
	}

	strfnd_wx f(menupath);
	
	wxMenu *basemenu = NULL;
	wxMenu *menu = NULL;
	wxString menuname;
	
	do{
		menuname = f.next(_T("|"));
		
		//fprintf(stderr, "finding menuname %s\n", (const char*)menuname.fn_str());
		int i;
		if(basemenu==NULL){
			i = menubar->FindMenu(menuname);
		}
		else{
			i = basemenu->FindItem(menuname);
		}

		if(i == wxNOT_FOUND){
			//fprintf(stderr, "creating a submenu ");
			menu = new wxMenu;
			menu->SetTitle(menuname);
			if(basemenu==NULL){
				//fprintf(stderr, "to menubar\n");
				menubar->Append(menu, menuname);
			}
			else{
				//fprintf(stderr, "to basemenu\n");
				basemenu->Append(wxID_ANY, menuname, menu);
			}
		}
		else{
			if(basemenu==NULL){
				menu = menubar->GetMenu(i);
			}
			else{
				wxMenuItem *item = basemenu->FindItem(i);
				if(item != NULL)
					menu = item->GetSubMenu();
			}
		}
		basemenu = menu;
	}
	while(f.atend() == false);

	return menu;
}

PyObject* add_menu_command(PyObject *self, PyObject *args)
{
	const char *menupath_c;
	const char *itemname_c;
	PyObject *obj;
	if(!PyArg_ParseTuple(args, "ssO:add_menu_command", &menupath_c, &itemname_c, &obj)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	if(!PyCallable_Check(obj)){
		PyErr_SetString(PyExc_TypeError, "parameter must be callable");
		return NULL;
	}
	
	wxString menupath = wxString(menupath_c, wxConvUTF8);
	wxString itemname = wxString(itemname_c, wxConvUTF8);

	wxMenu *menu = get_menu(menupath);
	if(menu == NULL){
		PyErr_SetString(PyExc_RuntimeError, "couldn't make menu");
		return NULL;
	}
	
	int id = wxGetApp().m_pythonapi.AddMenuCommand(obj);
	menu->Append(id, itemname);

	return Py_BuildValue("i", id);
}

PyObject* add_menu_check(PyObject *self, PyObject *args)
{
	const char *menupath_c;
	const char *itemname_c;
	PyObject *obj;
	int defaultstate;
	if(!PyArg_ParseTuple(args, "ssOi:add_menu_check", &menupath_c, &itemname_c, &obj, &defaultstate)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	if(!PyCallable_Check(obj)){
		PyErr_SetString(PyExc_TypeError, "parameter must be callable");
		return NULL;
	}

	wxString menupath = wxString(menupath_c, wxConvUTF8);
	wxString itemname = wxString(itemname_c, wxConvUTF8);

	wxMenu *menu = get_menu(menupath);
	if(menu == NULL){
		PyErr_SetString(PyExc_RuntimeError, "couldn't make menu");
		return NULL;
	}
	
	int id = wxGetApp().m_pythonapi.AddMenuCheck(obj);
	menu->AppendCheckItem(id, itemname);
	menu->Check(id, defaultstate);

	return Py_BuildValue("i", id);
}

PyObject* msgbox_info(PyObject *self, PyObject *args)
{
	const char *message;
	const char *title;
	if(!PyArg_ParseTuple(args, "ss", &message, &title)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	
	wxMessageBox(wxString(message, wxConvUTF8), wxString(title, wxConvUTF8),
			wxOK | wxICON_INFORMATION, wxGetApp().m_frame);

	Py_INCREF(Py_None);
	return Py_None;
}

const char *about_string =
    "jukumagic - A stripboard layout designer\n"
	"by Perttu Ahola <celeron55@gmail.com>\n"
	"Version 0.0.1\n\n"
	"Licensed under the GPLv3\n"
	"- If you don't have the source, get it\n"
	"from googlecode or by mailing me.";

PyObject* get_about_string(PyObject *self, PyObject *args)
{
	return Py_BuildValue("s", about_string);
}

PyObject* open_file_dialog(PyObject *self, PyObject *args)
{
	const char *caption;
	const char *filetypes;
	if(!PyArg_ParseTuple(args, "ss", &caption, &filetypes)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxFileDialog fd((wxWindow*)wxGetApp().m_frame, wxString(caption,
			wxConvUTF8), wxT(""), wxT(""), wxString(filetypes, wxConvUTF8),
			wxOPEN | wxFILE_MUST_EXIST);
	
	if(fd.ShowModal() == wxID_OK){
		return Py_BuildValue("is", true, (const char*)fd.GetPath().fn_str());
	}
	else{
		return Py_BuildValue("is", false, "");
	}
}

PyObject* save_file_dialog(PyObject *self, PyObject *args)
{
	const char *caption;
	const char *filepath;
	const char *filetypes;
	int ask_overwrite;
	if(!PyArg_ParseTuple(args, "sssi", &caption, &filepath, &filetypes, &ask_overwrite)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxFileDialog fd((wxWindow*)wxGetApp().m_frame,
			wxString(caption, wxConvUTF8), wxT(""),
			wxString(filepath, wxConvUTF8), wxString(filetypes, wxConvUTF8),
			wxSAVE | (ask_overwrite?wxOVERWRITE_PROMPT:0));
	
	if(fd.ShowModal() == wxID_OK){
		return Py_BuildValue("is", true, (const char*)fd.GetPath().fn_str());
	}
	else{
		return Py_BuildValue("is", false, "");
	}
}

PyObject* preferences_dialog(PyObject *self, PyObject *args)
{
	PreferencesDialog d(wxGetApp().m_frame, &wxGetApp().m_settings);
	if(d.ShowModal() == wxID_OK) wxGetApp().m_frame->Refresh();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* exit(PyObject *self, PyObject *args)
{
	wxGetApp().AnytimeExit();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* file_new(PyObject *self, PyObject *args)
{
	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxGetApp().NewProject(wxString(path, wxConvUTF8));
	//wxGetApp().m_current_file_path = wxString(path, wxConvUTF8);

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* file_open(PyObject *self, PyObject *args)
{
	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	wxGetApp().OpenProject(wxString(path, wxConvUTF8));

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* file_save(PyObject *self, PyObject *args)
{
	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	wxGetApp().SaveProject(wxString(path, wxConvUTF8));

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* file_close(PyObject *self, PyObject *args)
{
	wxGetApp().CloseProject();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* file_settings(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);
	
	ProjectSettingsDialog d(wxGetApp().m_frame, wxGetApp().m_project);
	if(d.ShowModal() == wxID_OK){
		wxGetApp().m_frame->RefreshBoard();
	}

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* refresh_board(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	
	wxGetApp().m_frame->RefreshBoard();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* load_netlist_spice(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);

	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxGetApp().m_project->m_netlist.LoadFromSpiceFile(path);
	wxGetApp().m_frame->SetNetlist(&wxGetApp().m_project->m_netlist);
	
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* load_partslist_gnetlist(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);

	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxGetApp().m_project->m_netlist.LoadFromPartslistFile(path);
	wxGetApp().m_frame->SetNetlist(&wxGetApp().m_project->m_netlist);
	
	Py_INCREF(Py_None);
	return Py_None;
}


/*PyObject* write_cut_csv(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);

	const char *path;
	if(!PyArg_ParseTuple(args, "s", &path)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxGetApp().m_project->m_board->SaveCutCSV(path);
	
	Py_INCREF(Py_None);
	return Py_None;
}*/

PyObject* get_control_state(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);

	return Py_BuildValue("i", wxGetApp().m_frame->GetControlState());
}

PyObject* get_shift_state(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);

	return Py_BuildValue("i", wxGetApp().m_frame->GetShiftState());
}

struct KeycodeTranslation
{
	wxUint32 keycode;
	const char *keyname;
}
static keycode_strings[] =
{
	{WXK_BACK,	"WXK_BACK"},
	{WXK_TAB,	"WXK_TAB"},
	{WXK_RETURN,	"WXK_RETURN"},
	{WXK_ESCAPE,	"WXK_ESCAPE"},
	{WXK_SPACE,	"WXK_SPACE"},
	{WXK_DELETE,	"WXK_DELETE"},

	{WXK_START,	"WXK_START"},
	{WXK_LBUTTON,	"WXK_LBUTTON"},
	{WXK_RBUTTON,	"WXK_RBUTTON"},
	{WXK_CANCEL,	"WXK_CANCEL"},
	{WXK_MBUTTON,	"WXK_MBUTTON"},
	{WXK_CLEAR,	"WXK_CLEAR"},
	{WXK_SHIFT,	"WXK_SHIFT"},
	{WXK_ALT,	"WXK_ALT"},
	{WXK_CONTROL,	"WXK_CONTROL"},
	{WXK_MENU,	"WXK_MENU"},
	{WXK_PAUSE,	"WXK_PAUSE"},
	{WXK_CAPITAL,	"WXK_CAPITAL"},
	{WXK_END,	"WXK_END"},
	{WXK_HOME,	"WXK_HOME"},
	{WXK_LEFT,	"WXK_LEFT"},
	{WXK_UP,	"WXK_UP"},
	{WXK_RIGHT,	"WXK_RIGHT"},
	{WXK_DOWN,	"WXK_DOWN"},
	{WXK_SELECT,	"WXK_SELECT"},
	{WXK_PRINT,	"WXK_PRINT"},
	{WXK_EXECUTE,	"WXK_EXECUTE"},
	{WXK_SNAPSHOT,	"WXK_SNAPSHOT"},
	{WXK_INSERT,	"WXK_INSERT"},
	{WXK_HELP,	"WXK_HELP"},
	{WXK_NUMPAD0,	"WXK_NUMPAD0"},
	{WXK_NUMPAD1,	"WXK_NUMPAD1"},
	{WXK_NUMPAD2,	"WXK_NUMPAD2"},
	{WXK_NUMPAD3,	"WXK_NUMPAD3"},
	{WXK_NUMPAD4,	"WXK_NUMPAD4"},
	{WXK_NUMPAD5,	"WXK_NUMPAD5"},
	{WXK_NUMPAD6,	"WXK_NUMPAD6"},
	{WXK_NUMPAD7,	"WXK_NUMPAD7"},
	{WXK_NUMPAD8,	"WXK_NUMPAD8"},
	{WXK_NUMPAD9,	"WXK_NUMPAD9"},
	{WXK_MULTIPLY,	"WXK_MULTIPLY"},
	{WXK_ADD,	"WXK_ADD"},
	{WXK_SEPARATOR,	"WXK_SEPARATOR"},
	{WXK_SUBTRACT,	"WXK_SUBTRACT"},
	{WXK_DECIMAL,	"WXK_DECIMAL"},
	{WXK_DIVIDE,	"WXK_DIVIDE"},
	{WXK_F1,	"WXK_F1"},
	{WXK_F2,	"WXK_F2"},
	{WXK_F3,	"WXK_F3"},
	{WXK_F4,	"WXK_F4"},
	{WXK_F5,	"WXK_F5"},
	{WXK_F6,	"WXK_F6"},
	{WXK_F7,	"WXK_F7"},
	{WXK_F8,	"WXK_F8"},
	{WXK_F9,	"WXK_F9"},
	{WXK_F10,	"WXK_F10"},
	{WXK_F11,	"WXK_F11"},
	{WXK_F12,	"WXK_F12"},
	{WXK_F13,	"WXK_F13"},
	{WXK_F14,	"WXK_F14"},
	{WXK_F15,	"WXK_F15"},
	{WXK_F16,	"WXK_F16"},
	{WXK_F17,	"WXK_F17"},
	{WXK_F18,	"WXK_F18"},
	{WXK_F19,	"WXK_F19"},
	{WXK_F20,	"WXK_F20"},
	{WXK_F21,	"WXK_F21"},
	{WXK_F22,	"WXK_F22"},
	{WXK_F23,	"WXK_F23"},
	{WXK_F24,	"WXK_F24"},
	{WXK_NUMLOCK,	"WXK_NUMLOCK"},
	{WXK_SCROLL,	"WXK_SCROLL"},
	{WXK_PAGEUP,	"WXK_PAGEUP"},
	{WXK_PAGEDOWN,	"WXK_PAGEDOWN"},

	{WXK_NUMPAD_SPACE,	"WXK_NUMPAD_SPACE"},
	{WXK_NUMPAD_TAB,	"WXK_NUMPAD_TAB"},
	{WXK_NUMPAD_ENTER,	"WXK_NUMPAD_ENTER"},
	{WXK_NUMPAD_F1,	"WXK_NUMPAD_F1"},
	{WXK_NUMPAD_F2,	"WXK_NUMPAD_F2"},
	{WXK_NUMPAD_F3,	"WXK_NUMPAD_F3"},
	{WXK_NUMPAD_F4,	"WXK_NUMPAD_F4"},
	{WXK_NUMPAD_HOME,	"WXK_NUMPAD_HOME"},
	{WXK_NUMPAD_LEFT,	"WXK_NUMPAD_LEFT"},
	{WXK_NUMPAD_UP,	"WXK_NUMPAD_UP"},
	{WXK_NUMPAD_RIGHT,	"WXK_NUMPAD_RIGHT"},
	{WXK_NUMPAD_DOWN,	"WXK_NUMPAD_DOWN"},
	{WXK_NUMPAD_PAGEUP,	"WXK_NUMPAD_PAGEUP"},
	{WXK_NUMPAD_PAGEDOWN,	"WXK_NUMPAD_PAGEDOWN"},
	{WXK_NUMPAD_END,	"WXK_NUMPAD_END"},
	{WXK_NUMPAD_BEGIN,	"WXK_NUMPAD_BEGIN"},
	{WXK_NUMPAD_INSERT,	"WXK_NUMPAD_INSERT"},
	{WXK_NUMPAD_DELETE,	"WXK_NUMPAD_DELETE"},
	{WXK_NUMPAD_EQUAL,	"WXK_NUMPAD_EQUAL"},
	{WXK_NUMPAD_MULTIPLY,	"WXK_NUMPAD_MULTIPLY"},
	{WXK_NUMPAD_ADD,	"WXK_NUMPAD_ADD"},
	{WXK_NUMPAD_SEPARATOR,	"WXK_NUMPAD_SEPARATOR"},
	{WXK_NUMPAD_SUBTRACT,	"WXK_NUMPAD_SUBTRACT"},
	{WXK_NUMPAD_DECIMAL,	"WXK_NUMPAD_DECIMAL"},
	{WXK_NUMPAD_DIVIDE,	"WXK_NUMPAD_DIVIDE"},

	{WXK_WINDOWS_LEFT,	"WXK_WINDOWS_LEFT"},
	{WXK_WINDOWS_RIGHT,	"WXK_WINDOWS_RIGHT"},
	{WXK_WINDOWS_MENU,	"WXK_WINDOWS_MENU"},
	{WXK_COMMAND,	"WXK_COMMAND"},

	{WXK_SPECIAL1,	"WXK_SPECIAL1"},
	{WXK_SPECIAL2,	"WXK_SPECIAL2"},
	{WXK_SPECIAL3,	"WXK_SPECIAL3"},
	{WXK_SPECIAL4,	"WXK_SPECIAL4"},
	{WXK_SPECIAL5,	"WXK_SPECIAL5"},
	{WXK_SPECIAL6,	"WXK_SPECIAL6"},
	{WXK_SPECIAL7,	"WXK_SPECIAL7"},
	{WXK_SPECIAL8,	"WXK_SPECIAL8"},
	{WXK_SPECIAL9,	"WXK_SPECIAL9"},
	{WXK_SPECIAL10,	"WXK_SPECIAL10"},
	{WXK_SPECIAL11,	"WXK_SPECIAL11"},
	{WXK_SPECIAL12,	"WXK_SPECIAL12"},
	{WXK_SPECIAL13,	"WXK_SPECIAL13"},
	{WXK_SPECIAL14,	"WXK_SPECIAL14"},
	{WXK_SPECIAL15,	"WXK_SPECIAL15"},
	{WXK_SPECIAL16,	"WXK_SPECIAL16"},
	{WXK_SPECIAL17,	"WXK_SPECIAL17"},
	{WXK_SPECIAL18,	"WXK_SPECIAL18"},
	{WXK_SPECIAL19,	"WXK_SPECIAL19"},
	{WXK_SPECIAL20,	"WXK_SPECIAL20"},
};

PyObject* add_accelerator(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);

	const char *modifier;
	const char *keycode_c;
	int menu_id;
	if(!PyArg_ParseTuple(args, "ssi", &modifier, &keycode_c, &menu_id)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	int keycode_i = -1;
	if(strlen(keycode_c) == 1){
		keycode_i = keycode_c[0];
	}
	else{
		for(unsigned int i=0; i<sizeof(keycode_strings)/sizeof(keycode_strings[0]); i++){
			if(strcmp(keycode_c, keycode_strings[i].keyname) == 0){
				keycode_i = keycode_strings[i].keycode;
				break;
			}
		}
	}

	if(keycode_i == -1){
		PyErr_SetString(PyExc_RuntimeError, "invalid keycode");
		return NULL;
	}

	int flags = wxACCEL_NORMAL;

	if(strcmp(modifier, "none")==0) flags = wxACCEL_NORMAL;
	else if(strcmp(modifier, "alt")==0) flags = wxACCEL_ALT;
	else if(strcmp(modifier, "ctrl")==0) flags = wxACCEL_CTRL;
	else if(strcmp(modifier, "shift")==0) flags = wxACCEL_SHIFT;
	else if(strcmp(modifier, "cmd")==0) flags = wxACCEL_CMD;
	else{
		PyErr_SetString(PyExc_RuntimeError, "invalid modifier");
		return NULL;
	}

	wxAcceleratorEntry entry(flags, keycode_i, menu_id);
	
	wxGetApp().m_frame->AddAcceleratorEntry(entry);

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* create_rats_nest(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);
	
	int only_selected_object;
	if(!PyArg_ParseTuple(args, "i", &only_selected_object)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	
	wxGetApp().m_project->CreateRatsNest(only_selected_object);
	wxGetApp().m_frame->Refresh();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* create_inverse_rats_nest(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);
	
	int only_selected_object;
	if(!PyArg_ParseTuple(args, "i", &only_selected_object)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	wxGetApp().m_project->CreateInvRatsNest(only_selected_object);
	wxGetApp().m_frame->Refresh();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* clear_rats_nest(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	pyapi_check_silent(wxGetApp().m_project->m_board->m_ratsnest);
	
	wxGetApp().m_project->m_board->m_ratsnest->Clear();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* set_mouse_mode(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	
	const char *mode_c;
	if(!PyArg_ParseTuple(args, "s", &mode_c)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	BoardWindowMouseMode newmode = MODE_INVALID;

	if(strcmp(mode_c, "select")==0) newmode = MODE_SELECT;
	else if(strcmp(mode_c, "add_wire")==0) newmode = MODE_ADD_JUMPER_WIRE;
	else if(strcmp(mode_c, "add_wire_bottom")==0) newmode = MODE_ADD_JUMPER_WIRE_BOTTOM;
	else if(strcmp(mode_c, "add_cut")==0) newmode = MODE_ADD_CUT;
	else if(strcmp(mode_c, "add_narrowcut")==0) newmode = MODE_ADD_NARROWCUT;
	else if(strcmp(mode_c, "add_resistor")==0) newmode = MODE_ADD_RESISTOR;
	else if(strcmp(mode_c, "add_diode")==0) newmode = MODE_ADD_DIODE;
	else if(strcmp(mode_c, "add_ceramic")==0) newmode = MODE_ADD_CERAMIC;
	else if(strcmp(mode_c, "move_component_head")==0) newmode = MODE_MOVE_COMPONENT_HEAD;
	else if(strcmp(mode_c, "delete")==0) newmode = MODE_DELETE;
	
	if(newmode == MODE_INVALID){
		PyErr_SetString(PyExc_RuntimeError, "invalid mode");
		return NULL;
	}

	wxGetApp().m_frame->SetMouseMode(newmode);
	
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* delete_selected(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	wxGetApp().m_project->m_board->DeleteSelectedObjects();
	
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* get_cuts(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	PyObject *list = PyList_New(0);
	if(list==NULL){
		return PyErr_NoMemory();
	}

	for(std::list<JukuObject*>::const_iterator i = wxGetApp().m_project->m_board->GetObjects().begin(); i != wxGetApp().m_project->m_board->GetObjects().end(); i++){
		if((*i)->GetType() != TYPE_CUT && (*i)->GetType() != TYPE_NARROWCUT) continue;
		BoardPoint p;
		if((*i)->GetType() != TYPE_CUT){
			p = ((Cut*)(*i))->GetBoardPos();
		}
		else{
			p = ((NarrowCut*)(*i))->GetBoardPos();
		}
		int cutx = (p.m_x+50)/100;
		//int cutx = wxGetApp().m_project->m_board->GetW()-1-(p.m_x+50)/100;
		int cuty = (p.m_y+50)/100;
		bool is_narrow = ((*i)->GetType() == TYPE_NARROWCUT);
		PyObject *node = Py_BuildValue("bii", is_narrow, cutx, cuty);
		if(node==NULL){
			Py_DECREF(list);
			return PyErr_NoMemory();
		}
		PyList_Append(list, node);
	}
	
	return list;
}

PyObject* get_holes(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	PyObject *list = PyList_New(0);
	if(list==NULL){
		return PyErr_NoMemory();
	}

	for(std::list<JukuObject*>::const_iterator i = wxGetApp().m_project->m_board->GetObjects().begin(); i != wxGetApp().m_project->m_board->GetObjects().end(); i++){
		if((*i)->GetType() == TYPE_JUMPER_WIRE){
			JumperWire *w = (JumperWire*)(*i);
			for(int i=1; i<=2; i++){
				BoardPoint wp = w->GetHeadPoint(i);
				PyObject *node = Py_BuildValue("ii", (wp.m_x+50)/100, (wp.m_y+50)/100);
				//PyObject *node = Py_BuildValue("ii", wxGetApp().m_project->m_board->GetW()-1-(wp.m_x+50)/100, (wp.m_y+50)/100);
				if(node==NULL){
					Py_DECREF(list);
					return PyErr_NoMemory();
				}
				PyList_Append(list, node);
			}
		}
		else if((*i)->GetType() == TYPE_COMPONENT){
			Component *c = (Component*)(*i);
			BoardPoint cp = c->GetBoardPos();
			Footprint *f = c->GetFootprint();
			if(f==NULL) continue;
			for(std::list<JukuObject*>::const_iterator i = f->m_pins.GetObjects().begin(); i != f->m_pins.GetObjects().end(); i++){
				if((*i)->GetType() != TYPE_PIN) continue;
				Pin *pin = (Pin*)(*i);
				int x = cp.m_x + pin->m_x;
				int y = cp.m_y + pin->m_y;
				PyObject *node = Py_BuildValue("ii", (x+50)/100, (y+50)/100);
				//PyObject *node = Py_BuildValue("ii", wxGetApp().m_project->m_board->GetW()-1-(x+50)/100, (y+50)/100);
				if(node==NULL){
					Py_DECREF(list);
					return PyErr_NoMemory();
				}
				PyList_Append(list, node);
			}
		}
	}
	
	return list;
}

PyObject* get_board_w(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	return Py_BuildValue("i", wxGetApp().m_project->m_board->GetW());
}

PyObject* get_board_h(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	return Py_BuildValue("i", wxGetApp().m_project->m_board->GetH());
}

PyObject* collect_overboard_components(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	wxGetApp().m_project->m_board->CollectOverBoardComponents();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* selection_rotate_90(PyObject *self, PyObject *args)
{
	pyapi_check_silent(wxGetApp().m_frame);
	pyapi_check_silent(wxGetApp().m_project);
	pyapi_check_silent(wxGetApp().m_project->m_board);
	
	//std::cout<<"BoardScrolledWindow::Rotate90Selection"<<std::endl;
	if(wxGetApp().m_project->m_board->GetSelectedObjects().empty()){
		//assert(0);
		//std::cout<<"wxGetApp().m_project->m_board->GetSelectedObjects().empty()"<<std::endl;
		return false;
	}
	int x1=BIG_INT_VALUE, y1=BIG_INT_VALUE, x2=SMALL_INT_VALUE, y2=SMALL_INT_VALUE;
	for(std::set<JukuObject*>::const_iterator i = wxGetApp().m_project->m_board->GetSelectedObjects().begin();
			i != wxGetApp().m_project->m_board->GetSelectedObjects().end(); i++){
		BoardRect r = (*i)->GetPinExtents();
		if(r.m_x < x1) x1 = r.m_x;
		if(r.m_y < y1) y1 = r.m_y;
		if(r.m_x + (int)r.m_w > x2) x2 = r.m_x + r.m_w;
		if(r.m_y + (int)r.m_h > y2) y2 = r.m_y + r.m_h;
	}
	//std::cout<<x1<<", "<<y1<<", "<<x2<<", "<<y2<<std::endl;
	int center_x = (x1+x2)/2;
	int center_y = (y1+y2)/2;
	//make sure the pins will snap to holes when rotated
	if(!((center_x % 100 == 0 && center_y % 100 == 0)
			|| ((center_x+50) % 100 == 0 && (center_y+50) % 100 == 0))){
		//this conditioning makes sure everything will be at the same place after four rotations
		if(center_y % 100 == 0) center_x += 50;
		else center_x -= 50;
	}
	BoardPoint origin(center_x, center_y);
	//std::cout<<origin.m_x<<", "<<origin.m_y<<std::endl;
	for(std::set<JukuObject*>::const_iterator i = wxGetApp().m_project->m_board->GetSelectedObjects().begin();
			i != wxGetApp().m_project->m_board->GetSelectedObjects().end(); i++){
		(*i)->Rotate(CCW, origin);
	}
	wxGetApp().m_frame->RefreshBoard();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* library_add_directory(PyObject *self, PyObject *args)
{
	const char *filepath_c;
	if(!PyArg_ParseTuple(args, "s", &filepath_c)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}
	
	wxString filepath(filepath_c, wxConvUTF8);

	wxGetApp().m_library.AddDir(filepath);
	
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* library_sort(PyObject *self, PyObject *args)
{
	wxGetApp().m_library.Sort();
	
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* library_add_footprint(PyObject *self, PyObject *args)
{
	PyObject *obj;
	const char *name_c;
	if(!PyArg_ParseTuple(args, "Os", &obj, &name_c)){
		PyErr_SetString(PyExc_RuntimeError, "wrong parameters");
		return NULL;
	}

	if(!PyObject_TypeCheck(obj, &PyApiFootprintType)){
		PyErr_SetString(PyExc_TypeError, "a footprint is required");
		return NULL;
	}
	
	wxString name(name_c, wxConvUTF8);

	PyApiFootprint *pyfootprint = (PyApiFootprint*)obj;

	if(pyfootprint->footprint == NULL){
		PyErr_SetString(PyExc_RuntimeError, "pyfootprint->footprint is NULL");
		return NULL;
	}

	Py_INCREF(pyfootprint);

	wxGetApp().m_library.AddFootprint(pyfootprint->footprint, name);
	
	Py_INCREF(Py_None);
	return Py_None;
}


/*
PyObject* exit(PyObject *self, PyObject *args)
{

	Py_INCREF(Py_None);
	return Py_None;
}

*/

#define method_noargs(name) {#name, name, METH_NOARGS, #name "()"}
#define method(name, params) {#name, name, METH_VARARGS, #name "(" params ")"}
#define method_doc(name, params, doc) {#name, name, METH_VARARGS, #name "(" params "); " doc}

/*
	IMPORTANT: Put at least the function name in the docstring.
	It is automatically copied to doc/python_api_funcs.txt.
	Everything has to be on one line.
*/
PyMethodDef JukuMethods[] =
{
	method_doc(set_setting, "name, value", "a list of settings is available in doc/settings.txt"),
	method_doc(get_setting, "name", "a list of settings is available in doc/settings.txt"),
	method_doc(add_menu_command, "menupath, itemname, function)", "returns id.\nfunction is func(id).\nmenupath format: menu|submenu|submenu2.\nMay not add accelerators correctly because adds menu items after adding submenus (wxWidgets)."),
	method_doc(add_menu_check, "menupath, itemname, function, defaultstate).", "returns id.\nfunction is func(id, state).\nmenupath format: menu|submenu|submenu2.\nMay not add accelerators correctly because adds menu items after adding submenus (wxWidgets)."),
	method(msgbox_info, "message, title"),
	method_noargs(get_about_string),
	method(open_file_dialog, "caption, filetypes"),
	method(save_file_dialog, "caption, filepath, filetypes, ask_overwrite"),
	method_noargs(preferences_dialog),
	method_noargs(exit),
	method_doc(file_new, "path", "path can be '' for default/unspecified"),
	method(file_open, "path"),
	method(file_save, "path"),
	method_noargs(file_close),
	method_noargs(file_settings),
	method_noargs(refresh_board),
	method(load_netlist_spice, "path"),
	method(load_partslist_gnetlist, "path"),
	//method(write_cut_csv, "path"),
	method_noargs(get_control_state),
	method_noargs(get_shift_state),
	method_doc(add_accelerator, "modifier, keycode, menu_id", "modifier = 'none'/'alt'/'ctrl'/'shift'/'cmd'\nkeycode = ascii (eg. 'a') / wxwidgets keycode (eg. 'WXK_HOME'), menu_id = id from add_menu_command"),
	method(create_rats_nest, "only_selected_object"),
	method(create_inverse_rats_nest, "only_selected_object"),
	method_noargs(clear_rats_nest),
	method_doc(set_mouse_mode, "mode", "mode = 'select'/'add_wire'/'add_wire_bottom'/'add_cut'/'add_narrowcut'/'add_resistor'/'add_diode'/'add_ceramic'/'move_component_head'/'delete'"),
	method_noargs(delete_selected),
	method_doc(get_cuts, "", "return value: list of tuples: (is_narrow, x, y) (coords flipped x-wise)"),
	method_doc(get_holes, "", "return value: list of tuples: x, y) (coords flipped x-wise)"),
	method_noargs(get_board_w),
	method_noargs(get_board_h),
	method_doc(collect_overboard_components, "", "Moves components that are located off the board to the nearest place on the board."),
	method_noargs(selection_rotate_90),
	method(library_add_directory, "dirpath"),
	method_noargs(library_sort),
	method_doc(library_add_footprint, "footprint, name", "footprint is of type juku.footprint. (look at the config script for example)"),
	{NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initjuku(void)
{
    if (PyType_Ready(&PyApiFootprintType) < 0)
        return;

	PyObject *m = Py_InitModule("juku", JukuMethods);
	if(m==NULL)
		return;

    Py_INCREF(&PyApiFootprintType);
    PyModule_AddObject(m, "footprint", (PyObject *)&PyApiFootprintType);
}


